package com.gy.hadoop.juc.lock;

/*
可重入锁/递归锁
    可重入锁又叫递归锁，指的同一个线程在**外层方法**获得锁时，进入**内层方法**会自动获取锁。也就是说，线程可以进入任何一个它已经拥有锁的代码块。比如`get`方法里面有`set`方法，两个方法都有同一把锁，得到了`get`的锁，就自动得到了`set`的锁。
    eg:
        lock.lock();
        lock.lock();
        try{
            someAction();
        }finally{
            lock.unlock();
            lock.unlock();
        }
自旋锁
    while (!atomicReference.compareAndSet(null, thread)) { }
读写锁/独占/共享锁
    **读锁**是**共享的**，**写锁**是**独占的**。`juc.ReentrantLock`和`synchronized`都是**独占锁**
其他的
    CountDownLatch：计数器执行--，直到count=0时,await处结束阻塞
    CyclicBarrier【鸣枪起跑】：计数器++（参与者await），直到count=指定值，之后终极目标执行（可选），最后所有线程开始执行
    Semaphore【3个停车位6辆车】：非独占锁，可以复用

`synchronized`关键字和`java.util.concurrent.locks.Lock`都能加锁，两者有什么区别呢？

1. **原始构成**：`sync`是JVM层面的，底层通过`monitorenter`和`monitorexit`来实现的。`Lock`是JDK API层面的。（`sync`一个enter会有两个exit，一个是正常退出，一个是异常退出）
2. **使用方法**：`sync`不需要手动释放锁，而`Lock`需要手动释放。
3. **是否可中断**：`sync`不可中断，除非抛出异常或者正常运行完成。`Lock`是可中断的，通过调用`interrupt()`方法。
4. **是否为公平锁**：`sync`只能是非公平锁，而`Lock`既能是公平锁，又能是非公平锁。
5. **绑定多个条件**：`sync`不能，只能随机唤醒。而`Lock`可以通过`Condition`来绑定多个条件，精确唤醒。
 */

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

public class Lock {


    public static void main(String[] args) throws InterruptedException {
        Editlock1 editlock1 = new Editlock1();
        new Thread(() -> {
            try {
                editlock1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                editlock1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t2").start();


        final Editlock myLock = new Editlock();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myLock.myUnLock();
        }, "AAA").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myLock.myUnLock();
        }, "BBB").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myLock.myUnLock();
        }, "CCC").start();

        NoMonopoly.countDownDemo();
        NoMonopoly.cyclicBarrierDemo();
        NoMonopoly.semaphoreDemo();
    }

    /**
     * 其他锁
     */
    private static class NoMonopoly {

        /**
         * countDownLatch案例 秦国灭6国
         */
        private static void countDownDemo() throws InterruptedException {
            CountDownLatch latch = new CountDownLatch(6);
            for (int i = 0; i < 6; i++) {
                System.out.println(Thread.currentThread().getName() + "\t国被灭");
                latch.countDown();
            }
            //TODO 此处阻塞
            latch.await();
            System.out.println(Thread.currentThread().getName() + "\t ******秦国一统华夏");
        }

        /**
         * CyclicBarrier案例 集齐7龙珠
         */
        private static void cyclicBarrierDemo() {
            CyclicBarrier barrier = new CyclicBarrier(7, () -> {
                System.out.println("**召唤神龙**");
            });

            for (int i = 0; i < 7; i++) {
                final int tempInt = i;
                new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() +
                            "\t 收集到第" + tempInt + "颗龙珠");
                    try {
                        barrier.await();
                        System.out.println(Thread.currentThread().getName() +
                                "\t 收集完龙珠后的操作");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }, String.valueOf(i)).start();
            }
        }

        /**
         * Semaphore 3个车位6辆车
         */
        private static void semaphoreDemo() {
            Semaphore semaphore = new Semaphore(3);
            for (int i = 1; i <= 6; i++) {
                new Thread(() -> {
                    try {
                        //占有资源
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "\t抢到车位");
                        try {
                            TimeUnit.SECONDS.sleep(3);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "\t停车3秒后离开车位");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //释放资源
                    finally {
                        semaphore.release();
                    }
                }, String.valueOf(i)).start();
            }
        }

    }


    /**
     * 完成自旋锁
     * cpu消耗大
     */
    private static class Editlock {

        private final AtomicReference<Thread> atomicReference = new AtomicReference<>();

        void myLock() {
            Thread currT = Thread.currentThread();
            System.out.println(Thread.currentThread().getName() + "\t" + " lock");
            for (; !atomicReference.compareAndSet(null, currT); ) {
            }
        }

        void myUnLock() {
            Thread currT = Thread.currentThread();
            atomicReference.compareAndSet(currT, null);
            System.out.println(Thread.currentThread().getName() + "\t" + " unLock");
        }
    }

    /**
     * 重入锁
     * synchronized和ReentrantLock都是重入锁，每次或取计数器+1
     * 一个线程或取到一个锁之后可以无消耗再次或取该锁
     */
    private static class Editlock1 implements Runnable {
        //Synchronized TEST

        public synchronized void sendSMS() {
            System.out.println(Thread.currentThread().getName() + "\t" + "sendSMS()");
            sendEmail();
        }

        public synchronized void sendEmail() {
            System.out.println(Thread.currentThread().getName() + "\t" + "sendEmail()");
        }

        //Reentrant TEST
        java.util.concurrent.locks.Lock lock = new ReentrantLock();

        @Override
        public void run() {
            get();
        }

        public void get() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t" + "get()");
                set();
            } finally {
                lock.unlock();
            }
        }

        public void set() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t" + "set()");
            } finally {
                lock.unlock();
            }
        }
    }

}
